import type {
  Attributes,
  Extensions,
  GlobalAttributes,
  ParentConfig,
  RawCommands,
  RawSelectors,
} from "./types.ts";
import type { Editor } from "./editor.ts";
import type { Plugin, Transaction } from "@rivet/state";
import type { MarkType, Node, NodeSpec, NodeType } from "@rivet/model";
import type { Mark } from "./Mark.ts";

export { Editor } from "./editor.ts";
export { Node } from "./Node.ts";
export { Mark } from "./Mark.ts";
export { Extension } from "./Extension.ts";
export * from "./types.ts";
export * from "./helpers/index.ts";
export * from "./utilities/index.ts";

/*
基础类型定义 留给 具体 实现进行扩展
*/

// deno-lint-ignore no-empty-interface
export interface Commands {}
// deno-lint-ignore no-empty-interface
export interface Selectors {}

export interface ExtensionConfig<Options = any, Storage = any> {
  [key: string]: any;

  /**
   * Name
   */
  name: string;

  /**
   * Priority
   */
  priority?: number;

  /**
   * Default options
   */
  defaultOptions?: Options;

  /**
   * Default Options
   */
  addOptions?: (this: {
    name: string;
    parent: Exclude<
      ParentConfig<ExtensionConfig<Options, Storage>>["addOptions"],
      undefined
    >;
  }) => Options;

  addStorage?: (this: {
    name: string;
    options: Options;
    parent: Exclude<
      ParentConfig<ExtensionConfig<Options, Storage>>["addStorage"],
      undefined
    >;
  }) => Storage;

  addGlobalAttributes?: (this: {
    name: string;
    options: Options;
    storage: Storage;
    parent: ParentConfig<
      ExtensionConfig<Options, Storage>
    >["addGlobalAttributes"];
  }) => GlobalAttributes | {};

  addCommands?: (this: {
    name: string;
    options: Options;
    storage: Storage;
    editor: Editor;
    parent: ParentConfig<ExtensionConfig<Options, Storage>>["addCommands"];
  }) => Partial<RawCommands>;

  addSelectors?: (this: {
    name: string;
    options: Options;
    storage: Storage;
    editor: Editor;
    parent: ParentConfig<ExtensionConfig<Options, Storage>>["addSelectors"];
  }) => Partial<RawSelectors>;

  /**
   * addRivetPlugins plugins
   */
  addRivetPlugins?: (this: {
    name: string;
    options: Options;
    storage: Storage;
    editor: Editor;
    parent: ParentConfig<
      ExtensionConfig<Options, Storage>
    >["addRivetPlugins"];
  }) => Plugin[];

  /**
   * Extensions
   */
  addExtensions?: (this: {
    name: string;
    options: Options;
    storage: Storage;
    parent: ParentConfig<ExtensionConfig<Options, Storage>>["addExtensions"];
  }) => Extensions;

  /**
   * Extend Node Schema
   */
  extendNodeSchema?:
    | ((
      this: {
        name: string;
        options: Options;
        storage: Storage;
        parent: ParentConfig<
          ExtensionConfig<Options, Storage>
        >["extendNodeSchema"];
      },
      extension: Node,
    ) => Record<string, any>)
    | null;

  /**
   * The editor is not ready yet.
   */
  onBeforeCreate?:
    | ((this: {
      name: string;
      options: Options;
      storage: Storage;
      editor: Editor;
      parent: ParentConfig<ExtensionConfig<Options, Storage>>["onBeforeCreate"];
    }) => void)
    | null;

  /**
   * The editor is ready.
   */
  onCreate?:
    | ((this: {
      name: string;
      options: Options;
      storage: Storage;
      editor: Editor;
      parent: ParentConfig<ExtensionConfig<Options, Storage>>["onCreate"];
    }) => void)
    | null;

  /**
   * The content has changed.
   */
  onUpdate?:
    | ((this: {
      name: string;
      options: Options;
      storage: Storage;
      editor: Editor;
      parent: ParentConfig<ExtensionConfig<Options, Storage>>["onUpdate"];
    }) => void)
    | null;

  /**
   * The editor state has changed.
   */
  onTransaction?:
    | ((
      this: {
        name: string;
        options: Options;
        storage: Storage;
        editor: Editor;
        parent: ParentConfig<
          ExtensionConfig<Options, Storage>
        >["onTransaction"];
      },
      props: {
        transaction: Transaction;
      },
    ) => void)
    | null;

  /**
   * The editor is destroyed.
   */
  onDestroy?:
    | ((this: {
      name: string;
      options: Options;
      storage: Storage;
      editor: Editor;
      parent: ParentConfig<ExtensionConfig<Options, Storage>>["onDestroy"];
    }) => void)
    | null;
}

export interface NodeConfig<Options = any, Storage = any> {
  [key: string]: any;

  /**
   * 名称
   */
  name: string;

  /**
   * 优先级
   */
  priority?: number;

  /**
   * Default options
   */
  defaultOptions?: Options;

  /**
   * Default Options
   */
  addOptions?: (this: {
    name: string;
    parent: Exclude<
      ParentConfig<NodeConfig<Options, Storage>>["addOptions"],
      undefined
    >;
  }) => Options;

  /**
   * Default Storage
   */
  addStorage?: (this: {
    name: string;
    options: Options;
    parent: Exclude<
      ParentConfig<NodeConfig<Options, Storage>>["addStorage"],
      undefined
    >;
  }) => Storage;

  /**
   * Global attributes
   */
  addGlobalAttributes?: (this: {
    name: string;
    options: Options;
    storage: Storage;
    parent: ParentConfig<NodeConfig<Options, Storage>>["addGlobalAttributes"];
  }) => GlobalAttributes | {};
  addCommands?: (this: {
    name: string;
    options: Options;
    storage: Storage;
    editor: Editor;
    type: NodeType;
    parent: ParentConfig<NodeConfig<Options, Storage>>["addCommands"];
  }) => Partial<RawCommands>;

  addSelectors?: (this: {
    name: string;
    options: Options;
    storage: Storage;
    editor: Editor;
    parent: ParentConfig<ExtensionConfig<Options, Storage>>["addSelectors"];
  }) => Partial<RawSelectors>;
  /**
   * 添加插件
   */
  addRivetPlugins?: (this: {
    name: string;
    options: Options;
    storage: Storage;
    editor: Editor;
    type: NodeType;
    parent: ParentConfig<NodeConfig<Options, Storage>>["addRivetPlugins"];
  }) => Plugin[];

  /**
   * 扩展
   */
  addExtensions?: (this: {
    name: string;
    options: Options;
    storage: Storage;
    parent: ParentConfig<NodeConfig<Options, Storage>>["addExtensions"];
  }) => Extensions;

  /**
   * Extend Node Schema
   */
  extendNodeSchema?:
    | ((
      this: {
        name: string;
        options: Options;
        storage: Storage;
        parent: ParentConfig<NodeConfig<Options, Storage>>["extendNodeSchema"];
      },
      extension: Node,
    ) => Record<string, any>)
    | null;

  /**
   * Extend Mark Schema
   */
  extendMarkSchema?:
    | ((
      this: {
        name: string;
        options: Options;
        storage: Storage;
        parent: ParentConfig<NodeConfig<Options, Storage>>["extendMarkSchema"];
      },
      extension: Node,
    ) => Record<string, any>)
    | null;

  /**
   * The editor is not ready yet.
   */
  onBeforeCreate?:
    | ((this: {
      name: string;
      options: Options;
      storage: Storage;
      editor: Editor;
      type: NodeType;
      parent: ParentConfig<NodeConfig<Options, Storage>>["onBeforeCreate"];
    }) => void)
    | null;

  /**
   * The editor is ready.
   */
  onCreate?:
    | ((this: {
      name: string;
      options: Options;
      storage: Storage;
      editor: Editor;
      type: NodeType;
      parent: ParentConfig<NodeConfig<Options, Storage>>["onCreate"];
    }) => void)
    | null;

  /**
   * The content has changed.
   */
  onUpdate?:
    | ((this: {
      name: string;
      options: Options;
      storage: Storage;
      editor: Editor;
      type: NodeType;
      parent: ParentConfig<NodeConfig<Options, Storage>>["onUpdate"];
    }) => void)
    | null;

  /**
   * The editor state has changed.
   */
  onTransaction?:
    | ((
      this: {
        name: string;
        options: Options;
        storage: Storage;
        editor: Editor;
        type: NodeType;
        parent: ParentConfig<NodeConfig<Options, Storage>>["onTransaction"];
      },
      props: {
        transaction: Transaction;
      },
    ) => void)
    | null;

  /**
   * The editor is destroyed.
   */
  onDestroy?:
    | ((this: {
      name: string;
      options: Options;
      storage: Storage;
      editor: Editor;
      type: NodeType;
      parent: ParentConfig<NodeConfig<Options, Storage>>["onDestroy"];
    }) => void)
    | null;

  /**
   * 是否为顶级节点
   */
  topNode?: boolean;

  /**
   * Content子类型约束
   */
  content?:
    | NodeSpec["content"]
    | ((this: {
      name: string;
      options: Options;
      storage: Storage;
      parent: ParentConfig<NodeConfig<Options, Storage>>["content"];
    }) => NodeSpec["content"]);

  /**
   * Marks 标记
   */
  marks?:
    | NodeSpec["marks"]
    | ((this: {
      name: string;
      options: Options;
      storage: Storage;
      parent: ParentConfig<NodeConfig<Options, Storage>>["marks"];
    }) => NodeSpec["marks"]);

  /**
   * Group 分组
   */
  group?:
    | NodeSpec["group"]
    | ((this: {
      name: string;
      options: Options;
      storage: Storage;
      parent: ParentConfig<NodeConfig<Options, Storage>>["group"];
    }) => NodeSpec["group"]);

  /**
   * 添加属性
   */
  addAttributes?: (
    this: {
      name: string;
      options: Options;
      storage: Storage;
      parent: ParentConfig<NodeConfig<Options, Storage>>["addAttributes"];
    },
  ) => Attributes | {};
}

export interface MarkConfig<Options = any, Storage = any> {
  [key: string]: any;

  /**
   * Name
   */
  name: string;

  /**
   * Priority
   */
  priority?: number;

  /**
   * Default options
   */
  defaultOptions?: Options;

  /**
   * Default Options
   */
  addOptions?: (this: {
    name: string;
    parent: Exclude<
      ParentConfig<MarkConfig<Options, Storage>>["addOptions"],
      undefined
    >;
  }) => Options;

  /**
   * Default Storage
   */
  addStorage?: (this: {
    name: string;
    options: Options;
    parent: Exclude<
      ParentConfig<MarkConfig<Options, Storage>>["addStorage"],
      undefined
    >;
  }) => Storage;

  /**
   * Global attributes
   */
  addGlobalAttributes?: (this: {
    name: string;
    options: Options;
    storage: Storage;
    parent: ParentConfig<MarkConfig<Options, Storage>>["addGlobalAttributes"];
  }) => GlobalAttributes | {};

  addCommands?: (this: {
    name: string;
    options: Options;
    storage: Storage;
    editor: Editor;
    type: MarkType;
    parent: ParentConfig<MarkConfig<Options, Storage>>["addCommands"];
  }) => Partial<RawCommands>;

  addSelectors?: (this: {
    name: string;
    options: Options;
    storage: Storage;
    editor: Editor;
    parent: ParentConfig<ExtensionConfig<Options, Storage>>["addSelectors"];
  }) => Partial<RawSelectors>;
  /**
   * Rivet plugins
   */
  addRivetPlugins?: (this: {
    name: string;
    options: Options;
    storage: Storage;
    editor: Editor;
    type: MarkType;
    parent: ParentConfig<MarkConfig<Options, Storage>>["addRivetPlugins"];
  }) => Plugin[];

  /**
   * Extensions
   */
  addExtensions?: (this: {
    name: string;
    options: Options;
    storage: Storage;
    parent: ParentConfig<MarkConfig<Options, Storage>>["addExtensions"];
  }) => Extensions;

  /**
   * Extend Node Schema
   */
  extendNodeSchema?:
    | ((
      this: {
        name: string;
        options: Options;
        storage: Storage;
        parent: ParentConfig<MarkConfig<Options, Storage>>["extendNodeSchema"];
      },
      extension: Node,
    ) => Record<string, any>)
    | null;

  /**
   * Extend Mark Schema
   */
  extendMarkSchema?:
    | ((
      this: {
        name: string;
        options: Options;
        storage: Storage;
        parent: ParentConfig<MarkConfig<Options, Storage>>["extendMarkSchema"];
      },
      extension: Mark,
    ) => Record<string, any>)
    | null;

  /**
   * The editor is not ready yet.
   */
  onBeforeCreate?:
    | ((this: {
      name: string;
      options: Options;
      storage: Storage;
      editor: Editor;
      type: MarkType;
      parent: ParentConfig<MarkConfig<Options, Storage>>["onBeforeCreate"];
    }) => void)
    | null;

  /**
   * The editor is ready.
   */
  onCreate?:
    | ((this: {
      name: string;
      options: Options;
      storage: Storage;
      editor: Editor;
      type: MarkType;
      parent: ParentConfig<MarkConfig<Options, Storage>>["onCreate"];
    }) => void)
    | null;

  /**
   * The content has changed.
   */
  onUpdate?:
    | ((this: {
      name: string;
      options: Options;
      storage: Storage;
      editor: Editor;
      type: MarkType;
      parent: ParentConfig<MarkConfig<Options, Storage>>["onUpdate"];
    }) => void)
    | null;

  /**
   * The selection has changed.
   */
  onSelectionUpdate?:
    | ((this: {
      name: string;
      options: Options;
      storage: Storage;
      editor: Editor;
      type: MarkType;
      parent: ParentConfig<MarkConfig<Options, Storage>>["onSelectionUpdate"];
    }) => void)
    | null;

  /**
   * The editor state has changed.
   */
  onTransaction?:
    | ((
      this: {
        name: string;
        options: Options;
        storage: Storage;
        editor: Editor;
        type: MarkType;
        parent: ParentConfig<MarkConfig<Options, Storage>>["onTransaction"];
      },
      props: {
        transaction: Transaction;
      },
    ) => void)
    | null;

  /**
   * The editor is destroyed.
   */
  onDestroy?:
    | ((this: {
      name: string;
      options: Options;
      storage: Storage;
      editor: Editor;
      type: MarkType;
      parent: ParentConfig<MarkConfig<Options, Storage>>["onDestroy"];
    }) => void)
    | null;

  /**
   * Attributes
   */
  addAttributes?: (
    this: {
      name: string;
      options: Options;
      storage: Storage;
      parent: ParentConfig<MarkConfig<Options, Storage>>["addAttributes"];
    },
  ) => Attributes | {};
}
